[アップデート] CodePipeline に新しく ECRBuildAndPublish, InspectorScan アクションが追加されました

[アップデート] CodePipeline に新しく ECRBuildAndPublish, InspectorScan アクションが追加されました

CodePipeline に新しく ECRBuildAndPublish, InspectorScan アクションが追加されました。 CodeBuild を使って自前で用意していた buildspec がマネージドなアクションに置き換えられそうなアップデートです。
Clock Icon2024.11.26

こんにちは!AWS 事業本部コンサルティング部のたかくに(@takakuni_)です。

CodePipeline に新しく ECRBuildAndPublish, InspectorScan アクションが追加されました。とてもいいですね。

https://aws.amazon.com/about-aws/whats-new/2024/11/aws-codepipeline-publishing-ecr-image-aws-inspectorscan-actions/

何が嬉しいのか

今まで CodePipeline でコンテナイメージの構築、Inspector Scan をパイプライン組み込みたい場合、自前で CodeBuild 等を用いて、実装する必要がありました。

コンテナイメージの構築

buildspec.yml
version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
      - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG

https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/sample-docker.html

Inspector のイメージスキャン

buildspec.yml
version: 0.2
env:
  shell: bash
  variables:
    DOCKER_BUILDKIT: '1'
    AWS_PAGER: ''
  exported-variables:
    - BUILD_URL

phases:
  install:
    commands:
      - aws --version
      - echo AWS CLI update...
      - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
      - unzip awscliv2.zip
      - ./aws/install --bin-dir /root/.pyenv/shims --install-dir /usr/local/aws-cli --update
      - aws --version
      - echo Install Amazon Inspector SBOM Generator...
      - curl -O https://amazon-inspector-sbomgen.s3.amazonaws.com/latest/linux/amd64/inspector-sbomgen.zip
      - unzip inspector-sbomgen.zip
      - mv inspector-sbomgen-* inspector-sbomgen-latest
      - chmod +x inspector-sbomgen-latest/linux/amd64/inspector-sbomgen
      - ./inspector-sbomgen-latest/linux/amd64/inspector-sbomgen --version
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  build:
    commands:
      - ./inspector-sbomgen-latest/linux/amd64/inspector-sbomgen container --image $IMAGE_URL --outfile /tmp/sbom.json --quiet
      - aws inspector-scan scan-sbom --sbom file:///tmp/sbom.json --output-format INSPECTOR --query 'sbom.vulnerabilities'
  post_build:
    commands:
      - export BUILD_URL=$CODEBUILD_BUILD_URL

https://dev.classmethod.jp/articles/reinvent2023-inspector-cicd-codebuild/

今回、この設定が簡素化され CodePipeline 側のマネージドアクションとして利用可能になりました。

とくに Insector Scan アクションに対しては「これだよ、これ!」ですごく嬉しいです。(去年の re:Invent を思い出しました)

CodeBuild の管理コスト(設定ファイル、環境イメージ、inspector-sbomgen)が減るのは、かなり嬉しいポイントではないでしょうか。

やってみる

それでは実際にリソースを作成して試してみましょう。

GitHub との接続

まずは以下のブログを参考に CodeConnection を利用して、GitHub との接続を行います。

(実際に試す場合は私のリポジトリをフォークする形で設定してください。)

https://dev.classmethod.jp/articles/aws-codepipeline-github-connection-v2/

パイプラインの作成

続いてパイプラインの構築です。

今回も Terraform を利用してパイプラインを作成します。

https://github.com/takakuni-classmethod/inspector-cicd-codebuild

今回はビルド、デプロイした ECR イメージを Inspector でスキャンするようなフローにしてみました。アクションがすべて CodePipeline で完結するためとてもスッキリしますね。

Untitled(94).png

なお、今回のアップデートは CodePipeline v1 ではサポートしていないようでした。

> │ Error: updating CodePipeline Pipeline (inspector-scan-pipeline): operation error CodePipeline: UpdatePipeline, https response error StatusCode: 400, RequestID: 069eb5aa-30ee-4abc-bcae-1d28d9f0ab94, InvalidActionDeclarationException: ECRBuildAndPublish Action can only be used with V2 pipelines.

IAM

今回の ECRBuildAndPublish, InspectorScan は CodePipeline がマネージドに利用しているビルド環境を利用します。

裏では CodeBuild が動いてますが、ユーザー側からは見えない CodeBuild Project を動かしているようです。

This action uses CodePipeline managed CodeBuild compute to run commands in a build environment. Running the action will incur separate charges in AWS CodeBuild.

https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECRBuildAndPublish.html

https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-InspectorScan.html

イメージは Commands に近いイメージです。

https://dev.classmethod.jp/articles/codepipeline-action-commands/

そのため、 IAM は CodePipeline に付与する必要があります。

ログを記録するためのポリシー

{
	"Effect": "Allow",
	"Action": [
		"logs:CreateLogGroup",
		"logs:CreateLogStream",
		"logs:PutLogEvents"
	],
	"Resource": [
		"arn:aws:logs:*:YOUR_AWS_ACCOUNT_ID:log-group:/aws/codepipeline/YOUR_PIPELINE_NAME",
		"arn:aws:logs:*:YOUR_AWS_ACCOUNT_ID:log-group:/aws/codepipeline/YOUR_PIPELINE_NAME:*"
	]
}

ECRBuildAndPublish

ドキュメントには "ecr:DescribeRepositories" が記載がないのですが、必要だったため足しています。

{
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"ecr:GetAuthorizationToken",
				"ecr:InitiateLayerUpload",
				"ecr:UploadLayerPart",
				"ecr:CompleteLayerUpload",
				"ecr:PutImage",
				"ecr:GetDownloadUrlForLayer",
				"ecr:BatchCheckLayerAvailability",
				"ecr:DescribeRepositories"
			],
			"Resource": "PrivateECR_Resource_ARN"
		},
		{
			"Effect": "Allow",
			"Action": [
				"ecr-public:GetAuthorizationToken",
				"ecr-public:DescribeRepositories",
				"ecr-public:InitiateLayerUpload",
				"ecr-public:UploadLayerPart",
				"ecr-public:CompleteLayerUpload",
				"ecr-public:PutImage",
				"ecr-public:BatchCheckLayerAvailability",
				"sts:GetServiceBearerToken"
			],
			"Resource": "PublicECR_Resource_ARN"
		},
		{
			"Effect": "Allow",
			"Action": ["sts:GetServiceBearerToken"],
			"Resource": "*"
		}
	]
}

InspectorScan

{
  "Effect": "Allow",
  "Action": "inspector-scan:ScanSbom",
  "Resource": "*"
},
{
  "Effect": "Allow",
  "Action": [
      "ecr:GetDownloadUrlForLayer",
      "ecr:BatchGetImage",
      "ecr:BatchCheckLayerAvailability"
  ],
  "Resource": "resource_ARN"
}

https://docs.aws.amazon.com/codepipeline/latest/userguide/security-iam.html

最終的な IAM ポリシーは以下になりました。Resource 句の部分は適宜変更してください。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"s3:PutObject",
				"s3:GetObject",
				"s3:GetObjectVersion",
				"s3:GetBucketAcl",
				"s3:GetBucketLocation"
			],
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Action": [
				"logs:CreateLogGroup",
				"logs:CreateLogStream",
				"logs:PutLogEvents"
			],
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Action": ["codestar-connections:UseConnection"],
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Action": [
				"ecr:GetAuthorizationToken",
				"ecr:InitiateLayerUpload",
				"ecr:UploadLayerPart",
				"ecr:CompleteLayerUpload",
				"ecr:PutImage",
				"ecr:GetDownloadUrlForLayer",
				"ecr:BatchCheckLayerAvailability",
				"ecr:DescribeRepositories"
			],
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Action": [
				"ecr-public:GetAuthorizationToken",
				"ecr-public:DescribeRepositories",
				"ecr-public:InitiateLayerUpload",
				"ecr-public:UploadLayerPart",
				"ecr-public:CompleteLayerUpload",
				"ecr-public:PutImage",
				"ecr-public:BatchCheckLayerAvailability",
				"sts:GetServiceBearerToken"
			],
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Action": ["sts:GetServiceBearerToken"],
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Action": "inspector-scan:ScanSbom",
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Action": [
				"ecr:GetDownloadUrlForLayer",
				"ecr:BatchGetImage",
				"ecr:BatchCheckLayerAvailability"
			],
			"Resource": "*"
		}
	]
}

ECRBuildAndPublish

各アクションの内容に移ります。まずは ECRBuildAndPublish から。

https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECRBuildAndPublish.html

ImageTags はデフォルトの場合、 latest タグになります。

If a value for ImageTags is not specified, the value defaults to latest.

https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECRBuildAndPublish.html#action-reference-ECRBuildAndPublish-config

コミット ID をイメージタグに付与している場合はマッチしないので、コミット ID を拾ってくる必要があります。

そのため、今回は Source ステージから変数で受け渡すよう設定します。

codepipeline.tf
#################################################
# CodePipeline
#################################################
resource "aws_codepipeline" "main" {
  name          = "${local.prefix}-pipeline"
  pipeline_type = "V2"

  role_arn = aws_iam_role.codepipeline.arn

  artifact_store {
    location = aws_s3_bucket.artifact.bucket
    type     = "S3"
  }

  stage {
    name = "Source"
    action {
      name             = "Source"
      category         = "Source"
+      namespace        = "Source"
      owner            = "AWS"
      provider         = "CodeStarSourceConnection"
      version          = 1
      output_artifacts = ["source_output"]
      configuration = {
        ConnectionArn    = var.connection_arn
        FullRepositoryId = "takakuni-classmethod/inspector-cicd-codebuild" # 任意の値を入力
        BranchName       = "v2"
      }
    }
  }

  stage {
    name = "Build"
    action {
      name            = "Build"
      category        = "Build"
      namespace       = "Build"
      owner           = "AWS"
      provider        = "ECRBuildAndPublish"
      version         = 1
      input_artifacts = ["source_output"]

      configuration = {
        ECRRepositoryName = aws_ecr_repository.main.name
        DockerFilePath    = "./docker"
+        ImageTags         = "#{Source.CommitId}"
      }
    }
  }

  stage {
    name = "Scan"
    action {
      name             = "Scan"
      category         = "Invoke"
      namespace        = "Scan"
      owner            = "AWS"
      provider         = "InspectorScan"
      version          = 1
      input_artifacts  = []
      output_artifacts = ["scan_output"]

      configuration = {
        InspectorRunMode  = "ECRImageScan"
        ECRRepositoryName = aws_ecr_repository.main.name
        ImageTag          = "#{Source.CommitId}"
      }
    }
  }

  stage {
    name = "Approval"
    action {
      name     = "Approval"
      category = "Approval"
      owner    = "AWS"
      provider = "Manual"
      version  = 1

      configuration = {
        CustomData = "Container image scan result."
        # ExternalEntityLink = "#{Scan.BUILD_URL}"
      }
    }
  }
}

InspectorScan

続いて InspectorScan に関してです。

InspectorRunMode は SourceCodeScan, ECRImageScan があります。

今回は ECRImageScan を選びますが、 SourceCodeScan でコンテナイメージのビルド前に確認できるのもいいですね。

https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-InspectorScan.html

Build ステージから、イメージタグを持って来れるとベストでしたが、ECRImageDigestId, ECRRepositoryName のみサポートしていたため、Source ステージから変数を持ってきました。

codepipeline.tf
#################################################
# CodePipeline
#################################################
resource "aws_codepipeline" "main" {
  name          = "${local.prefix}-pipeline"
  pipeline_type = "V2"

  role_arn = aws_iam_role.codepipeline.arn

  artifact_store {
    location = aws_s3_bucket.artifact.bucket
    type     = "S3"
  }

  stage {
    name = "Source"
    action {
      name             = "Source"
      category         = "Source"
+      namespace        = "Source"
      owner            = "AWS"
      provider         = "CodeStarSourceConnection"
      version          = 1
      output_artifacts = ["source_output"]
      configuration = {
        ConnectionArn    = var.connection_arn
        FullRepositoryId = "takakuni-classmethod/inspector-cicd-codebuild" # 任意の値を入力
        BranchName       = "v2"
      }
    }
  }

  stage {
    name = "Build"
    action {
      name            = "Build"
      category        = "Build"
      namespace       = "Build"
      owner           = "AWS"
      provider        = "ECRBuildAndPublish"
      version         = 1
      input_artifacts = ["source_output"]

      configuration = {
        ECRRepositoryName = aws_ecr_repository.main.name
        DockerFilePath    = "./docker"
        ImageTags         = "#{Source.CommitId}"
      }
    }
  }

  stage {
    name = "Scan"
    action {
      name             = "Scan"
      category         = "Invoke"
      namespace        = "Scan"
      owner            = "AWS"
      provider         = "InspectorScan"
      version          = 1
      input_artifacts  = []
      output_artifacts = ["scan_output"]

      configuration = {
        InspectorRunMode  = "ECRImageScan"
        ECRRepositoryName = aws_ecr_repository.main.name
+        ImageTag          = "#{Source.CommitId}"
      }
    }
  }

  stage {
    name = "Approval"
    action {
      name     = "Approval"
      category = "Approval"
      owner    = "AWS"
      provider = "Manual"
      version  = 1

      configuration = {
        CustomData = "Container image scan result."
      }
    }
  }
}

https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECRBuildAndPublish.html#w21aac54c15c21

結果を確認

コードをプッシュして CodePipeline をみてみましょう。

CodePipeline

2024-11-26 at 14.30.24-inspector-scan-pipeline  CodePipeline  ap-northeast-1@2x.png

InspectorScan

スキャン結果がログに出ていますね。重要度別に表示されていることがわかります。

2024-11-26 at 14.45.39-inspector-scan-pipeline  CodePipeline  ap-northeast-1.png

結果の詳細はどこにある?

出力アーティファクト(S3)に保管されてます。 CycloneDX 形式で SBOM が生成されており、 vulnerabilities の配列に記録されています。

{
	"bomFormat": "CycloneDX",
	"components": [
		{
			"bom-ref": "comp-1",
			"name": "Amazon Linux",
			"type": "operating-system",
			"version": "2023"
		},
		{
			"bom-ref": "comp-2",
			"name": "tzdata",
			"properties": [
				{
					"name": "amazon:inspector:sbom_scanner:info",
					"value": "Component skipped: no rules found."
				}
			],
			"purl": "pkg:rpm/amazon/tzdata@2023c-1.amzn2023.0.1?arch=noarch\u0026distro=2023\u0026epoch=0\u0026source=tzdata-2023c-1.amzn2023.0.1.src.rpm",
			"type": "application",
			"version": "2023c-1.amzn2023.0.1"
		},
		{
			"bom-ref": "comp-3",
			"name": "pcre2-syntax",
			"purl": "pkg:rpm/amazon/pcre2-syntax@10.40-1.amzn2023.0.2?arch=noarch\u0026distro=2023\u0026epoch=0\u0026source=pcre2-10.40-1.amzn2023.0.2.src.rpm",
			"type": "application",
			"version": "10.40-1.amzn2023.0.2"
		}
	],
	"metadata": {
		"properties": [
			{
				"name": "amazon:inspector:sbom_scanner:critical_vulnerabilities",
				"value": "5"
			},
			{
				"name": "amazon:inspector:sbom_scanner:high_vulnerabilities",
				"value": "31"
			},
			{
				"name": "amazon:inspector:sbom_scanner:medium_vulnerabilities",
				"value": "54"
			},
			{
				"name": "amazon:inspector:sbom_scanner:low_vulnerabilities",
				"value": "4"
			},
			{
				"name": "amazon:inspector:sbom_scanner:other_vulnerabilities",
				"value": "0"
			}
		],
		"timestamp": "2024-11-26T05:40:24.497Z",
		"tools": {
			"services": [
				{
					"name": "Amazon Inspector Scan SBOM API",
					"version": "56008c36+5ce64b41+1f1b76d5"
				}
			]
		}
	},
	"serialNumber": "urn:uuid:79f2b0e1-e1cb-49ff-a891-14756810599b",
	"specVersion": "1.5",
	"vulnerabilities": [
		{
			"advisories": [
				{
					"url": "https://lists.debian.org/debian-lts-announce/2023/12/msg00015.html"
				},
				{
					"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UOGXU25FMMT2X6UUITQ7EZZYMJ42YWWD/"
				},
				{
					"url": "https://hackerone.com/reports/2212193"
				},
				{
					"url": "https://www.debian.org/security/2023/dsa-5587"
				},
				{
					"url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3ZX3VW67N4ACRAPMV2QS2LVYGD7H2MVE/"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:2094"
				},
				{
					"url": "https://ubuntu.com/security/notices/USN-6535-1"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:2093"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:1383"
				},
				{
					"url": "https://www.cve.org/CVERecord?id=CVE-2023-46218"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:2092"
				},
				{
					"url": "https://alas.aws.amazon.com/AL2023/ALAS-2024-606.html"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:1317"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:0428"
				},
				{
					"url": "https://alas.aws.amazon.com/AL2/ALAS-2024-2531.html"
				},
				{
					"url": "https://ubuntu.com/security/notices/USN-6641-1"
				},
				{
					"url": "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1057646"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:0452"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:0585"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:0434"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:1316"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:1129"
				},
				{
					"url": "https://access.redhat.com/errata/RHSA-2024:1601"
				}
			],
			"affects": [
				{
					"ref": "comp-95"
				},
				{
					"ref": "comp-96"
				}
			],
			"analysis": {
				"state": "in_triage"
			},
			"bom-ref": "vuln-1",
			"created": "2023-10-19T01:00:12Z",
			"description": "This flaw allows a malicious HTTP server to set \"super cookies\" in curl that are then passed back to more origins than what is otherwise allowed or possible. This allows a site to set cookies that then would get sent to different and unrelated sites and domains.\n\nIt could do this by exploiting a mixed case flaw in curl's function that verifies a given cookie domain against the Public Suffix List (PSL). For example a cookie could be set with `domain=co.UK` when the URL used a lower case hostname `curl.co.uk`, even though `co.uk` is listed as a PSL domain.",
			"id": "CVE-2023-46218",
			"properties": [
				{
					"name": "amazon:inspector:sbom_scanner:priority",
					"value": "moderate"
				},
				{
					"name": "amazon:inspector:sbom_scanner:priority_intelligence",
					"value": "unverified"
				},
				{
					"name": "amazon:inspector:sbom_scanner:fixed_version:comp-95",
					"value": "0:8.5.0-1.amzn2023.0.2"
				},
				{
					"name": "amazon:inspector:sbom_scanner:fixed_version:comp-96",
					"value": "0:8.5.0-1.amzn2023.0.2"
				}
			],
			"published": "2023-12-07T01:15:07Z",
			"ratings": [
				{
					"method": "other",
					"score": 8.6e-4,
					"severity": "none",
					"source": {
						"name": "EPSS",
						"url": "https://api.first.org/data/v1/epss?cve=CVE-2023-46218"
					},
					"vector": "model:v2023.03.01,date:2024-11-25T00:00:00+0000"
				},
				{
					"method": "CVSSv31",
					"score": 6.5,
					"severity": "medium",
					"source": {
						"name": "Amazon Linux",
						"url": "https://alas.aws.amazon.com/cve/json/v1/CVE-2023-46218.json"
					},
					"vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N"
				},
				{
					"method": "CVSSv31",
					"score": 6.5,
					"severity": "medium",
					"source": {
						"name": "NVD",
						"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-46218"
					},
					"vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N"
				}
			],
			"references": [
				{
					"id": "ALAS2023-2024-606",
					"source": {
						"name": "ALAS2023",
						"url": "https://alas.aws.amazon.com/AL2023/ALAS-2024-606.html"
					}
				}
			],
			"source": {
				"name": "NVD",
				"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-46218"
			},
			"updated": "2024-01-25T14:15:26Z"
		}
	]
}

まとめ

以上、「CodePipeline に新しく ECRBuildAndPublish, InspectorScan アクションが追加されました。」でした。

CodePipeline のアクションに追加され、CodeBuild や IAM ロールなど登場人物が減るのは嬉しいですね。

ぜひ活用していきたいです。このブログがどなたかの参考になれば幸いです。

AWS 事業本部コンサルティング部のたかくに(@takakuni_)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.